#include "cdglwidget.h"

#include "shader.h"
#include "model.h"

#include <QSurfaceFormat>
#include <QOpenGLContext>
#include <QMouseEvent>
#include <QKeyEvent>

class QMouseEvent;
class QWheelEvent;
class QTimerEvent;

struct draw_type DT;

struct move_type MT;

struct texture_state TS;

struct animation_state AS;

struct light_type LT;

extern const char* vertexShaderCodeNew;
extern const char* fragmentShaderCodeNew;

unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

// lamp data
float vertices_lamp[] = {
    // positions
    -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    -0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,

    -0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,

    -0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,
    -0.5f, -0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,

    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,

    -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,
    -0.5f, -0.5f, -0.5f,

    -0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f
};

cdglwidget::cdglwidget(QWidget *parent):
    camera(QVector3D(0.0f, 0.0f, 3.0f), QVector3D(0.0f, 1.0f, 0.0f))
{
    Q_UNUSED(parent);

    view.setToIdentity();
    model.setToIdentity();
    model.translate(QVector3D(0.0f, -0.2f, 0.0f));  // translate it down so it's at the center of the scene
    model.scale(QVector3D(0.015f, 0.015f, 0.015f));	// it's a bit too big for our scene, so scale it down
//    // cow test
//    model.translate(QVector3D(0.0f, 0.0f, 0.0f));
//    model.scale(QVector3D(0.005f, 0.005f, 0.005f));
}

cdglwidget::~cdglwidget() {
    delete ourShader;
    delete ourModel;
}

void cdglwidget::initializeGL()
{
    initializeOpenGLFunctions();

    // 新建着色器
    ourShader = new Shader("1.model_loading.vs", "1.model_loading.fs");

    // 加载茶壶模型
    ourModel = new Model("teapot.obj");
    //ourModel = new Model("cow.obj");
    ourModel->localModel = model;

    // 灯的着色器
    lampShader = new Shader("6.lamp.vs", "6.lamp.fs");

    glGenVertexArrays(1, &lightVAO);
    glGenBuffers(1, &lightVBO);

    // bind data, position attribute
    glBindVertexArray(lightVAO);
    glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_lamp), vertices_lamp, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

    // fixed light position
    lightPos.setX(1.5f);
    lightPos.setY(1.3f);
    lightPos.setZ(2.5f);
}

void cdglwidget::paintGL()
{
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // lamp
    if (LT.l_type == 1) {
        lampShader->use();
        projection.setToIdentity();
        projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        lampShader->setMat4("projection", projection);
        view = camera.GetViewMatrix();
        lampShader->setMat4("view", view);
        QMatrix4x4 m;
        m.setToIdentity();
        m.translate(lightPos);
        m.scale(0.2f); // a smaller cube: lamp
        lampShader->setMat4("model", m);
        glBindVertexArray(lightVAO);
        glPolygonMode(GL_FRONT, GL_FILL);
        glPolygonMode(GL_BACK, GL_FILL);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }

    // don't forget to enable shader before setting uniforms
    ourShader->use();

    // view/projection transformations
    projection.setToIdentity();
    projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    view = camera.GetViewMatrix();
    ourShader->setMat4("projection", projection);
    ourShader->setMat4("view", view);

    if (AS.isAnimating == 1) {
        float angle = 0.5f;
        model.rotate(angle, QVector3D(0.0f, 1.0f, 0.0f));
        ourModel->localModel = model;
    } else if (AS.isAnimating == 2) {
        if (AS.openCap == 0) {
            ourModel->localModel = model;
        }
        if (AS.openCap >= 1 && AS.openCap < 51) {
            // 35.0f
            ourModel->localModel.translate(QVector3D(0.0f, 0.7f, 0.0f));
        }
        if (AS.openCap >= 1 && AS.openCap < 51) {
            // 25.0f
            ourModel->localModel.rotate(0.5f, QVector3D(0.0f, 0.0f, 1.0f));
        }
        if (AS.openCap >= 51 && AS.openCap < 76) {
            // 12.5f
            ourModel->localModel.translate(QVector3D(-cos(qDegreesToRadians(25.0f)) * 0.5f,
                                                     sin(qDegreesToRadians(25.0f)) * 0.5f, 0.0f));
        }
        if (AS.openCap >= 76 && AS.openCap < 126) {
            // 25.0f
            ourModel->localModel.translate(QVector3D(-0.25f, 0.0f, 0.0f));
        }
        if (AS.openCap >= 76 && AS.openCap < 126) {
            // 65.0f
            ourModel->localModel.rotate(1.3f, QVector3D(0.0f, 0.0f, 1.0f));
        }
        if (AS.openCap >= 126 && AS.openCap < 176) {
            // 25.0f
            ourModel->localModel.translate(QVector3D(0.0f, 0.5f, 0.0f));
        }
        if (AS.openCap >= 126 && AS.openCap < 176) {
            // 65.0f
            ourModel->localModel.translate(QVector3D(0.0f, 35.0f, 0.0f));
            ourModel->localModel.rotate(1.3f, QVector3D(0.0f, 0.0f, 1.0f));
            ourModel->localModel.translate(QVector3D(0.0, -35.0f, 0.0f));
        }

        AS.openCap++;
        if (AS.openCap >= 200) AS.openCap = 0;
    } else if (AS.isAnimating == 3) {
        if (AS.makeTea == 0) {
            model.setToIdentity();
            model.translate(QVector3D(0.0f, -0.2f, 0.0f));
            model.scale(QVector3D(0.015f, 0.015f, 0.015f));
            ourModel->localModel = model;
        }
        // ----------***----------- open cup cap
        if (AS.makeTea >= 1 && AS.makeTea < 51) {
            // 45.0f;
            ourModel->localModel.translate(QVector3D(0.0f, 0.9f, 0.0f));
        }
        if (AS.makeTea >= 1 && AS.makeTea < 51) {
            // 35.0f
            ourModel->localModel.rotate(0.7f, QVector3D(0.0f, 0.0f, 1.0f));
        }
        // ----------***----------- close cup cap
        if (AS.makeTea >= 101 && AS.makeTea < 151) {
            ourModel->localModel.rotate(-0.7, QVector3D(0.0f, 0.0f, 1.0f));
        }
        if (AS.makeTea >= 101 && AS.makeTea < 151) {
            ourModel->localModel.translate(QVector3D(0.0f, -0.9f, 0.0f));
        }
        // ----------***----------- take up tea-pot
        if (AS.makeTea >= 151 && AS.makeTea < 201) {
            // 22.0f
            model.translate(QVector3D(0.0f, 0.44f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 201 && AS.makeTea < 251) {
            // 15.0f, 20.0f
            model.translate(QVector3D(0.3f, 0.4f, 0.0f));
            ourModel->localModel = model;
        }
        // ----------***----------- pour out
        if (AS.makeTea >= 251 && AS.makeTea < 301) {
            // 45.0f
            model.rotate(-1.0f, QVector3D(0.0f, 0.0f, 1.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 301 && AS.makeTea < 321) {
            // 6.0f
            model.translate(QVector3D(0.3f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        // ----------***-----------
        // first turn
        if (AS.makeTea >= 321 && AS.makeTea < 341) {
            // 16.0f
            model.translate(QVector3D(-cos(qDegreesToRadians(45.0f)) * 0.8f,
                                      -sin(qDegreesToRadians(45.0f)) * 0.8f, -0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 341 && AS.makeTea < 361) {
            // 16.0f
            model.translate(QVector3D(0.0f, 0.0f, 2 * 0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 361 && AS.makeTea < 381) {
            // 16.0f
            model.translate(QVector3D(cos(qDegreesToRadians(45.0f)) * 0.8f,
                                      sin(qDegreesToRadians(45.0f)) * 0.8f, -0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        // second turn
        if (AS.makeTea >= 381 && AS.makeTea < 401) {
            // 16.0f,
            model.translate(QVector3D(-cos(qDegreesToRadians(52.5f)) * 0.8f,
                                      -sin(qDegreesToRadians(52.5f)) * 0.8f, -0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 401 && AS.makeTea < 421) {
            // 16.0f
            model.translate(QVector3D(0.0f, 0.0f, 2 * 0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 421 && AS.makeTea < 441) {
            // 16.0f
            model.translate(QVector3D(cos(qDegreesToRadians(52.5f)) * 0.8f,
                                      sin(qDegreesToRadians(52.5f)) * 0.8f, -0.8f));

            model.rotate(-0.125f, QVector3D(0.0f, 0.0f, 1.0f));
            model.translate(QVector3D(-0.05f, 0.0f, 0.0f));
            ourModel->localModel = model;
        }
        // ----------***-----------
        // first drop
        if (AS.makeTea >= 441 && AS.makeTea < 461) {
            // 16.0f
            model.translate(QVector3D(-cos(qDegreesToRadians(60.0f)) * 0.8f,
                                      -sin(qDegreesToRadians(60.0f)) * 0.8f, -0.8f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 441 && AS.makeTea < 451) {
            // 8.0f
            model.translate(QVector3D(0.0f, 0.8f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 451 && AS.makeTea < 461) {
            // 8.0f
            model.translate(QVector3D(0.0f, -0.8f, 0.0f));
            ourModel->localModel = model;
        }
        // second drop
        if (AS.makeTea >= 461 && AS.makeTea < 481) {
            // 16.0f
            model.translate(QVector3D(0.0f, 0.0f, 2 * 0.8f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 461 && AS.makeTea < 471) {
            // 8.0f
            model.translate(QVector3D(0.0f, 0.8f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 471 && AS.makeTea < 481) {
            // 8.0f
            model.translate(QVector3D(0.0f, -0.8f, 0.0f));
            ourModel->localModel = model;
        }
        // third drop
        if (AS.makeTea >= 481 && AS.makeTea < 501) {
            // 16.0f
            model.translate(QVector3D(cos(qDegreesToRadians(60.0f)) * 0.8f,
                                      sin(qDegreesToRadians(60.0f)) * 0.8f, -0.8f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 481 && AS.makeTea < 491) {
            // 8.0f
            model.translate(QVector3D(0.0f, 0.8f, 0.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 491 && AS.makeTea < 501) {
            // 8.0f
            model.translate(QVector3D(0.0f, -0.8f, 0.0f));
            ourModel->localModel = model;
        }
        // ----------***----------- back
        if (AS.makeTea >= 501 && AS.makeTea < 551) {
            // 65.0f
            model.rotate(-1.3f, QVector3D(0.0f, 0.0f, -1.0f));
            ourModel->localModel = model;
        }
        if (AS.makeTea >= 551 && AS.makeTea < 601) {
            // 15.0f, 42.0f
            model.translate(QVector3D(-0.3f, -0.79500f, 0.0f));
            ourModel->localModel = model;
        }

        AS.makeTea++;
        if (AS.makeTea >= 620) AS.makeTea = 0;
    } else if (AS.isAnimating == 4) {
        if (AS.dream == 0) {
            model.setToIdentity();
            model.translate(QVector3D(0.0f, -0.2f, 0.0f));
            model.scale(QVector3D(0.015f, 0.015f, 0.015f));
            ourModel->localModel = model;
        } else if (AS.dream >= 1 && AS.dream < 50) {
            // Y
            model.scale(QVector3D(1.0f, pow(0.4f, 1.0f/50), 1.0f));
            ourModel->localModel = model;
        } else if (AS.dream >= 51 && AS.dream < 101) {
            // Y big
            model.scale(QVector3D(1.0f, pow(1/0.4f, 1.0f/50), 1.0f));
            ourModel->localModel = model;
        } else if (AS.dream >= 101 && AS.dream < 151) {
            // Z
            model.scale(QVector3D(1.0f, 1.0f, pow(0.4f, 1.0f/50)));
            ourModel->localModel = model;
        } else if (AS.dream >= 151 && AS.dream < 201) {
            // X
            model.scale(QVector3D(pow(1/0.4f, 1.0f/50), 1.0f, 1.0f));
            ourModel->localModel = model;
        } else if (AS.dream >= 201 && AS.dream < 251) {
            // Z big
            model.scale(QVector3D(1.0f, 1.0f, pow(1/0.4f, 1.0f/50)));
            ourModel->localModel = model;
        } else if (AS.dream >= 251 && AS.dream < 301) {
            // X small
            model.scale(QVector3D(pow(0.4f, 1.0f/50), 1.0f, 1.0f));
            ourModel->localModel = model;
        } else if (AS.dream >= 301 && AS.dream < 351) {
            // cap Y+45.0f
            ourModel->localModel.translate(QVector3D(0.0f, 0.9f, 0.0f));
            ourModel->localModel.scale(QVector3D(pow(1/0.4f, 1.0f/50), pow(0.4f, 1.0f/50), pow(1/0.4f, 1.0f/50)));
        } else if (AS.dream >= 351 && AS.dream < 401) {
            ourModel->localModel.scale(QVector3D(pow(0.4f, 1.0f/50), 1.0f, pow(0.4f, 1.0f/50)));
        } else if (AS.dream >= 401 && AS.dream < 451) {
            ourModel->localModel.translate(QVector3D(0.0f, -0.9f, 0.0f));
            ourModel->localModel.scale(QVector3D(pow(0.4f, 1.0f/50), pow(1/0.4f, 1.0f/50), pow(0.4f, 1.0f/50)));
        } else if (AS.dream >= 451 && AS.dream < 501) {
            model.scale(QVector3D(pow(0.4f, 1.0f/50), 1.0f, pow(0.4f, 1.0f/50)));
        } else if (AS.dream >= 501 && AS.dream < 551) {
            model.scale(QVector3D(pow(1/0.4f, 1.0f/50), pow(1/0.4f, 1.0f/50), pow(1/0.4f, 1.0f/50)));
            ourModel->localModel = model;
        } else if (AS.dream >= 551 && AS.dream < 601) {
            model.scale(QVector3D(1.0f, pow(0.4f, 1.0f/50), 1.0f));
        } else if (AS.dream >= 601 && AS.dream < 651) {
            ourModel->localModel.scale(QVector3D(1.0f, pow(0.4f, 1.0f/50), 1.0f));
        }

        AS.dream++;
        if (AS.dream >= 670) AS.dream = 0;
    }

    ourModel->drawType = DT.type;

    ourModel->lampType = LT.l_type;
    ourModel->lightPos = lightPos;
    ourModel->cameraPos = camera.Position;
    ourModel->cameraFront = camera.Front;

    ourModel->texEnable = TS.texEnable;
    ourModel->orgColor = TS.orgColor;

    // render the loaded model
    ourShader->setMat4("model", model);
    ourModel->Draw(*ourShader);
}

void cdglwidget::resizeGL(int w, int h)
{
    SCR_WIDTH = w;
    SCR_HEIGHT = h;
    glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
}

void cdglwidget::mousePressEvent(QMouseEvent *event) {
    MT.lastX = event->x();
    MT.lastY = event->y();
}

void cdglwidget::mouseMoveEvent(QMouseEvent *event)
{
    if (MT.move_type == 1) {
        MT.xoffset = event->x() - MT.lastX;
        MT.yoffset = MT.lastY - event->y(); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessMouseMovement(MT.xoffset, MT.yoffset);
    } else if (MT.move_type == 2) {
        float sensitivity = 0.2f;
        MT.r_xoffset += sensitivity * (event->x() - MT.lastX);
        MT.r_yoffset += sensitivity * (MT.lastY - event->y()); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessRotation(MT.r_xoffset, MT.r_yoffset);
    }
    update();
}

void cdglwidget::mouseReleaseEvent(QMouseEvent *event) {
    Q_UNUSED(event);
    update();
}

void cdglwidget::wheelEvent(QWheelEvent *event) {
    // 缩放功能
    camera.ProcessMouseScroll(event->delta() * 0.012);
    update();
}

void cdglwidget::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed = 0.05;
    if (event->key() == Qt::Key_W)
        camera.Position += cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_S)
        camera.Position -= cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_A)
        camera.Position -= QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    if (event->key() == Qt::Key_D)
        camera.Position += QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    update();
}

void cdglwidget::initFpsCamera()
{
    camera.initFpsCamera();
}

void cdglwidget::timerEvent(QTimerEvent *event)
{
    // for animation
    Q_UNUSED(event);
    update();
}

void cdglwidget::setXpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(len, 0.0f, 0.0f));
    update();
}

void cdglwidget::setXnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(-len, 0.0f, 0.0f));
    update();
}

void cdglwidget::setYpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, len, 0.0f));
    update();
}

void cdglwidget::setYnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, -len, 0.0f));
    update();
}

void cdglwidget::setZpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, 0.0f, len));
    update();
}

void cdglwidget::setZnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, 0.0f, -len));
    update();
}
